/*
        Copyright 2019 Gaurav Kumar

        Licensed under the Apache License, Version 2.0 (the "License");
        you may not use this file except in compliance with the License.
        You may obtain a copy of the License at
        http://www.apache.org/licenses/LICENSE-2.0
        Unless required by applicable law or agreed to in writing, software
        distributed under the License is distributed on an "AS IS" BASIS,
        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        See the License for the specific language governing permissions and
        limitations under the License.
*/
package com.gauravk.bubblenavigation;

import com.gauravk.bubblenavigation.listener.BubbleNavigationChangeListener;
import com.gauravk.bubblenavigation.listener.IBubbleNavigation;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * BubbleNavigationLinearView
 *
 * @since 2021-04-09
 */
public class BubbleNavigationLinearView extends DirectionalLayout
        implements Component.ClickedListener, IBubbleNavigation, Component.BindStateChangedListener {
    private static final HiLogLabel LABEL =
            new HiLogLabel(HiLog.LOG_APP, 0x00202, BubbleNavigationLinearView.class.getSimpleName());

    private static final int MIN_ITEMS = 2;
    private static final int MAX_ITEMS = 5;

    private ArrayList<BubbleToggleView> bubbleNavItems = new ArrayList<>();
    private BubbleNavigationChangeListener navigationChangeListener;

    private int currentActiveItemPosition = 0;
    private boolean isLoadPreviousState = false;

    private HashMap<Integer, String> pendingBadgeUpdate;

    public EventHandler mEventHandler = null;

    /**
     * Constructors
     *
     * @param context 上下文
     */
    public BubbleNavigationLinearView(Context context) {
        super(context);
        init(context, null);
    }

    /**
     * Constructors
     *
     * @param context 上下文
     * @param attrs   属性
     */
    public BubbleNavigationLinearView(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**
     * Constructors
     *
     * @param context      上下文
     * @param attrs        属性
     * @param defStyleAttr 自定义样式
     */
    public BubbleNavigationLinearView(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, null);
        init(context, attrs);
    }

    /**
     * Initialize
     *
     * @param context current context
     * @param attrs   custom attributes
     */
    private void init(Context context, AttrSet attrs) {
        setBindStateChangedListener(this);
        if (mEventHandler == null) {
            mEventHandler = new EventHandler(EventRunner.getMainEventRunner());
        }
        mEventHandler.postTask(() -> updateChildNavItems());
    }

    /**
     * Finds Child Elements of type {@link BubbleToggleView} and adds them to {@link #bubbleNavItems}
     */
    private void updateChildNavItems() {
        bubbleNavItems = new ArrayList<>();
        for (int index = 0; index < getChildCount(); ++index) {
            Component view = getComponentAt(index);
            if (view instanceof BubbleToggleView) {
                BubbleToggleView btv = (BubbleToggleView) view;
                bubbleNavItems.add(btv);
            } else {
                HiLog.info(LABEL, "Cannot have child bubbleNavItems other than BubbleToggleView");
                return;
            }
        }

        if (bubbleNavItems.size() < MIN_ITEMS) {
            HiLog.info(LABEL, "The bubbleNavItems list should have at least 2 bubbleNavItems of BubbleToggleView");
        } else if (bubbleNavItems.size() > MAX_ITEMS) {
            HiLog.info(LABEL, "The bubbleNavItems list should not have more than 5 bubbleNavItems of BubbleToggleView");
        }

        setClickListenerForItems();
        setInitialActiveState();
        updateMeasurementForItems();

        // update the badge count
        if (pendingBadgeUpdate != null && bubbleNavItems != null) {
            for (Integer key : pendingBadgeUpdate.keySet()) {
                setBadgeValue(key, pendingBadgeUpdate.get(key));
            }
            pendingBadgeUpdate.clear();
        }
    }

    /**
     * Makes sure that ONLY ONE child {@link #bubbleNavItems} is active
     */
    private void setInitialActiveState() {
        if (bubbleNavItems == null) {
            return;
        }

        boolean foundActiveElement = false;

        // find the initial state
        if (!isLoadPreviousState) {
            for (int i = 0; i < bubbleNavItems.size(); i++) {
                if (bubbleNavItems.get(i).isActive() && !foundActiveElement) {
                    foundActiveElement = true;
                    currentActiveItemPosition = i;
                } else {
                    bubbleNavItems.get(i).setInitialState(false);
                }
            }
        } else {
            for (BubbleToggleView view : bubbleNavItems) {
                view.setInitialState(false);
            }
        }
    }

    /**
     * Update the measurements of the child components {@link #bubbleNavItems}
     */
    private void updateMeasurementForItems() {
        int numChildElements = bubbleNavItems.size();
        if (numChildElements > 0) {
            int calculatedEachItemWidth =
                    (getEstimatedWidth() - (getPaddingRight() + getPaddingLeft())) / numChildElements;
            for (BubbleToggleView btv : bubbleNavItems) {
                btv.updateMeasurements(calculatedEachItemWidth);
            }
        }
    }

    /**
     * Sets {OnClickListener} for the child views
     */
    private void setClickListenerForItems() {
        for (BubbleToggleView btv : bubbleNavItems) {
            btv.setClickedListener(this);
        }
    }

    /**
     * Gets the Position of the Child from {@link #bubbleNavItems} from its id
     *
     * @param id of view to be searched
     * @return position of the Item
     */
    private int getItemPositionById(int id) {
        for (int i = 0; i < bubbleNavItems.size(); i++) {
            if (id == bubbleNavItems.get(i).getId()) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Set the navigation change listener {@link BubbleNavigationChangeListener}
     *
     * @param navigationChangeListener sets the passed parameters as listener
     */
    @Override
    public void setNavigationChangeListener(BubbleNavigationChangeListener navigationChangeListener) {
        this.navigationChangeListener = navigationChangeListener;
    }

    /**
     * Gets the current active position
     *
     * @return active item position
     */
    @Override
    public int getCurrentActiveItemPosition() {
        return currentActiveItemPosition;
    }

    /**
     * Sets the current active item
     *
     * @param position current position change
     */
    @Override
    public void setCurrentActiveItem(int position) {
        if (bubbleNavItems == null) {
            currentActiveItemPosition = position;
            return;
        }

        if (currentActiveItemPosition == position) {
            return;
        }

        if (position < 0 || position >= bubbleNavItems.size()) {
            return;
        }

        BubbleToggleView btv = bubbleNavItems.get(position);
        btv.callOnClick();
    }

    /**
     * Sets the badge value
     *
     * @param position current position change
     * @param value    value to be set in the badge
     */
    @Override
    public void setBadgeValue(int position, String value) {
        if (bubbleNavItems != null && bubbleNavItems.size() > 0) {
            BubbleToggleView btv = bubbleNavItems.get(position);
            if (btv != null) {
                btv.setBadgeText(value);
            }
        } else {
            if (pendingBadgeUpdate == null) {
                pendingBadgeUpdate = new HashMap<>();
            }
            pendingBadgeUpdate.put(position, value);
        }
    }

    @Override
    public void onClick(Component v) {
        int changedPosition = getItemPositionById(v.getId());
        if (changedPosition >= 0) {
            if (changedPosition == currentActiveItemPosition) {
                return;
            }
            BubbleToggleView currentActiveToggleView = bubbleNavItems.get(currentActiveItemPosition);
            if (currentActiveToggleView != null && !currentActiveToggleView.isCanClick()) {
                return;
            }
            BubbleToggleView newActiveToggleView = bubbleNavItems.get(changedPosition);
            if (currentActiveToggleView != null) {
                currentActiveToggleView.toggle();
            }
            if (newActiveToggleView != null) {
                newActiveToggleView.toggle();
            }

            // changed the current active position
            currentActiveItemPosition = changedPosition;

            if (navigationChangeListener != null) {
                navigationChangeListener.onNavigationChanged(v, currentActiveItemPosition);
            }
        } else {
            HiLog.info(LABEL, "Selected id not found! Cannot toggle");
        }
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        if (bubbleNavItems != null) {
            bubbleNavItems.clear();
            bubbleNavItems = null;
        }
    }
}
